﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Server
{
    class Server
    {
        private TcpListener m_serverSocket;
        private List<TcpClient> m_clientsSockets;
        private List<User> m_users;
        private List<Room> m_rooms;
        private int m_clientCount;
        private int m_roomCount;
        public Server(int port)
        {
            m_clientsSockets = new List<TcpClient>(10);
            m_users = new List<User>(10);
            m_rooms = new List<Room>(10);
            m_serverSocket = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
        }
        public void start()
        {
            try
            {
                // start listening
                m_serverSocket.Start(10);
                // begin accepting one client (doesnt block)
                m_serverSocket.BeginAcceptTcpClient(new AsyncCallback(incomingConn), m_serverSocket);
                Console.WriteLine("Server Started....");
            }
            catch (SocketException ex)
            {
                // write the error if any
                Console.Write(ex.ToString());
            }
        }

        public void incomingConn(IAsyncResult iar)
        {
            try
            {
                //get the socket for the connected client
                TcpClient client = m_serverSocket.EndAcceptTcpClient(iar);
                // add to the list of sockets
                m_clientsSockets.Add(client);
                // create a user instance and associate it with the socket
                User user = new User(client, m_clientCount);
                //send welcome message
                string msg = "Welcome to Ibrahim server you are identified as UNKOWN" + m_clientCount;
                Interlocked.Increment(ref m_clientCount);
                SendMsgToClient(msg, user);
                // add the user to the list of users
                m_users.Add(user);
                Console.WriteLine("a user was connected, ip: " + client.Client.RemoteEndPoint.ToString());
                //wait for data asynchronusly
                user.m_socket.Client.BeginReceive(user.m_buffer, 0, user.m_buffer.Length, SocketFlags.None, OnDataReceived, user);
                // begin accepting another client asynchronusly
                m_serverSocket.BeginAcceptTcpClient(new AsyncCallback(incomingConn), m_serverSocket);
            }
            catch
            {
            }
        }
        public void OnDataReceived(IAsyncResult asyn)
        {
            //  get the user
            User user = (User)asyn.AsyncState;
            int charsCount = user.m_socket.Client.EndReceive(asyn);
            char[] chars = new char[charsCount + 1];
            System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
            int charLen = d.GetChars(user.m_buffer, 0, charsCount, chars, 0);
            String msg = new string(chars);
            //System.Console.WriteLine(user.m_name + ": " + msg);
            takeAction(msg, user);
            // continue waiting for data
            user.m_socket.Client.BeginReceive(user.m_buffer, 0,
                    user.m_buffer.Length,
                    SocketFlags.None,
                    OnDataReceived,
                    user);
        }

        void takeAction(String msg, User user)
        {
            String[] separatedMsg = (msg.Trim()).Split(' ');
            if (separatedMsg[0].CompareTo("join") == 0)
            {
                if (separatedMsg.Length < 2)
                {
                    return;
                }
                for (int i = 0; i < m_rooms.Count; i++)
                {
                    if (m_rooms[i].m_name.CompareTo(separatedMsg[1]) == 0)
                    {
                        if (m_rooms[i].addUser(user) == true)
                        {
                            user.SendMsgToClient("room: " + separatedMsg[1] + " joined successfully");
                        }
                        return;
                    }
                }
                user.SendMsgToClient("room: " + separatedMsg[1] + " doesn't exist");
            }
            else if (separatedMsg[0].CompareTo("create") == 0)
            {
                if (separatedMsg.Length < 2)
                {
                    return;
                }
                for(int i = 0; i < m_rooms.Count; i++)
                {
                    if (m_rooms[i].m_name.CompareTo(separatedMsg[1]) == 0)
                    {
                        user.SendMsgToClient("room: " + separatedMsg[1] + " already exit");
                        return;
                    }
                }
                Room room = new Room (user, separatedMsg[1]);
                m_rooms.Add(room);
                user.SendMsgToClient("room: " + separatedMsg[1] + " was created successfuly");
            }
            else if (separatedMsg[0].CompareTo("leave") == 0)
            {
                if (separatedMsg.Length < 2)
                {
                    return;
                }
                for (int i = 0; i < m_rooms.Count; i++)
                {
                    if (m_rooms[i].m_name.CompareTo(separatedMsg[1]) == 0)
                    {
                        if (m_rooms[i].removeUser(user) == true)
                        {
                            user.SendMsgToClient("room: " + separatedMsg[1] + " was left successfuly");
                        }
                        else
                        {
                            user.SendMsgToClient("you are not in room: " + separatedMsg[1]);
                        }
                    }
                }
            }
            else if (separatedMsg[0].CompareTo("myname") == 0)
            {
                if (separatedMsg.Length < 2)
                {
                    return;
                }
                for (int i = 0; i < m_users.Count; i++)
                {
                    if (m_users[i].m_name.CompareTo(separatedMsg[1]) == 0)
                    {
                        user.SendMsgToClient(separatedMsg[1] + " is already taken by another user, please chose another name");
                        return;
                    }
                }
                user.m_name = separatedMsg[1];
            }
            else if (separatedMsg[0].CompareTo("chmag") == 0)
            {
                if (separatedMsg.Length < 3)
                {
                    return;
                }
                for (int i = 0; i < m_rooms.Count; i++)
                {
                    if (m_rooms[i].m_name.CompareTo(separatedMsg[1]) == 0)
                    {
                        // roomname, user: message
                        m_rooms[i].broadcast(m_rooms[i].m_name + ", " +
                            user.m_name + ": " +
                            msg.Substring(separatedMsg[0].Length + separatedMsg[1].Length + 2));
                        return;
                    }
                }
                user.SendMsgToClient("room: " + separatedMsg[1] + " doesn't exist");
            }
            else
            {
            }
        }
        void SendMsgToClient(string msg, User user)
        {
            // Convert the reply to byte array
            byte[] buffer = System.Text.Encoding.ASCII.GetBytes(msg + "\n\r");
            user.m_socket.Client.Send(buffer);
        }

        void broadcast(string msg)
        {
            byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);
            for (int i = 0; i < m_users.Count(); i++)
            {
                m_users[i].m_socket.Client.Send(byData);
            }
        }
    }
}
